<?php

namespace App\Http\Api\Services;

use App\Exceptions\BadRequest as BadRequestException;
use App\Models\Connect as ConnectModel;
use App\Models\User as UserModel;
use App\Repos\Connect as ConnectRepo;
use App\Repos\User as UserRepo;
use App\Services\Auth\Api as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Services\OAuth\QQ as QQAuth;
use App\Services\OAuth\WeiBo as WeiBoAuth;
use App\Services\OAuth\WeiXin as WeiXinAuth;
use App\Validators\Account as AccountValidator;

class OAuth extends Service
{
    public function bindLogin(array $openUser, string $account, string $password): string
    {

        $validator = new AccountValidator();

        $user = $validator->checkUserLogin($account, $password);

        $this->handleConnectRelation($user, $openUser);

        $this->handleLoginNotice($user);

        $auth = $this->getAppAuth();

        $token = $auth->saveAuthInfo($user);

        return $token;
    }

    public function bindRegister(array $openUser)
    {
        $registerService = new RegisterService();

        $account = $registerService->handle();

        $userRepo = new UserRepo();

        $user = $userRepo->findById($account->id);

        $this->handleConnectRelation($user, $openUser);

        $this->handleLoginNotice($user);

        $auth = $this->getAppAuth();

        $token = $auth->saveAuthInfo($user);

        return $token;
    }

    public function bindCurrent(array  $openUser)
    {
        $user = $this->getLoginUser();

        $this->handleConnectRelation($user, $openUser);
    }

    public function login(ConnectModel $connect): string
    {
        $userRepo = new UserRepo();

        $user = $userRepo->findById($connect->user_id);

        if (!$user) {
            throw new BadRequestException('account.not_found');
        }

        $this->handleLoginNotice($user);

        $auth = $this->getAppAuth();

        $token = $auth->saveAuthInfo($user);

        return $token;
    }

    public function getAuthorizeUrl(int $provider)
    {
        return $this->getOAuthService($provider)
            ->getAuthorizeUrl();
    }

    public function getOpenUserInfo(int $provider, string $code)
    {
        $auth = $this->getOAuthService($provider);

        $token = $auth->getAccessToken($code);

        $openId = $auth->getOpenId($token);

        return $auth->getUserInfo($token, $openId);
    }

    public function getConnectRelation(int $provider, string $openId)
    {
        $connectRepo = new ConnectRepo();

        return $connectRepo->findByOpenId($openId, $provider);
    }

    public function getOAuthService(int $provider)
    {
        switch ($provider) {
            case ConnectModel::PROVIDER_QQ:
                return new QQAuth();
            case ConnectModel::PROVIDER_WEIXIN:
                return new WeiXinAuth();
            case ConnectModel::PROVIDER_WEIBO:
                return new WeiBoAuth();
            default:
                throw new \Exception('Invalid OAuth Provider');
        }
    }

    protected function getAppAuth(): AuthService
    {
        /**
         * @var $auth AuthService
         */
        $auth = $this->getDI()->get('auth');

        return $auth;
    }

    protected function handleConnectRelation(UserModel $user, array $openUser)
    {
        $connectRepo = new ConnectRepo();

        $connect = $connectRepo->findByOpenId($openUser['id'], $openUser['provider']);

        if ($connect) {

            $connect->open_name = $openUser['name'];
            $connect->open_avatar = $openUser['avatar'];

            if ($connect->user_id != $user->id) {
                $connect->user_id = $user->id;
            }

            $connect->update();

        } else {

            $connect = new ConnectModel();

            $connect->user_id = $user->id;
            $connect->open_id = $openUser['id'];
            $connect->open_name = $openUser['name'];
            $connect->open_avatar = $openUser['avatar'];
            $connect->provider = $openUser['provider'];

            $connect->create();
        }
    }

    protected function handleLoginNotice(UserModel $user)
    {
        $service = new AccountLoginNoticeService();

        $service->createTask($user);
    }

}
